package com.renfei.multimediatask.ui.task7;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;

import androidx.annotation.RequiresApi;

import com.renfei.multimediatask.R;
import com.renfei.multimediatask.base.BaseActivity;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class AudioAacEncodecActivity extends BaseActivity {

    @BindView(R.id.startRecord)
    Button startRecord;
    @BindView(R.id.stopRecord)
    Button stopRecord;
    @BindView(R.id.ll_btn_layout)
    RelativeLayout llBtnLayout;

    private int sampleRateInHz = 44100;   //采样率
    private int channelConfig = AudioFormat.CHANNEL_IN_MONO;   //声道数
    private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;   //采样格式(存储位数， 8位 16位)


    private AudioRecordThread audioRecordThread;
    private AudioEncodecThread audioEncodecThread;

    private String targetFilePath;

    /**
     * 使用一个队列将录制的数据存储起来
     */
    private ArrayBlockingQueue queue;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_audio_aac_encodec;
    }

    @Override
    protected void initListener() {
        targetFilePath = getParentFilePath() + "audio_aac_encodec.aac";

        queue = new ArrayBlockingQueue(1024);

        audioEncodecThread = new AudioEncodecThread(new WeakReference<>(this));
        audioRecordThread = new AudioRecordThread(new WeakReference<>(this));
    }


    @OnClick({R.id.startRecord, R.id.stopRecord})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.startRecord:
                start();
                break;
            case R.id.stopRecord:
                stop();
                break;
        }
    }

    private boolean isStrat = false;

    private void start() {
        if (isStrat) {
            return;
        }

        audioRecordThread.start();
        audioEncodecThread.start();
        isStrat = true;
    }

    private void stop() {
        audioRecordThread.stopRecording();
        audioEncodecThread.stopEncodec();
        isStrat = false;
    }


    private static class AudioData {
        private ByteBuffer buffer;
        private int size;
        private long prevOutputPTSUs;
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (null!=audioRecordThread){
            audioRecordThread.release();
        }
        if (audioEncodecThread!=null){
            audioEncodecThread.release();
        }
    }

    static class AudioRecordThread extends Thread {
        private static final boolean Debug = false;
        private static final String TAG = AudioRecordThread.class.getSimpleName();

        private WeakReference<AudioAacEncodecActivity> weakReference;


        //是否正在录制
        private boolean isRecording = false;

        private AudioRecord audioRecord;
        private int audioSource;   //音频来源
        private int sampleRateInHz;   //采样率
        private int channelConfig;   //声道数
        private int audioFormat;   //采样格式(存储位数， 8位 16位)
        private int bufferSizeInBytes;   //最小缓存大小


        public AudioRecordThread(WeakReference<AudioAacEncodecActivity> weakReference) {
            this.weakReference = weakReference;

            this.sampleRateInHz = weakReference.get().sampleRateInHz;
            this.channelConfig = weakReference.get().channelConfig;
            this.audioFormat = weakReference.get().audioFormat;

            initAudioRecord();
        }

        private void initAudioRecord() {
            bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
            audioRecord = new AudioRecord(
                    audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes
            );
            Log.e("RecordThread 初始化时 ", audioRecord.getState() + "");
        }

        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void run() {
            super.run();
            if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
                if (Debug) Log.e("RecordThread ", audioRecord.getState() + "");
                return;
            }
            audioRecord.startRecording();
            isRecording = true;


            if (Debug) Log.e(TAG, "音频录制开始 ");


            while (isRecording) {
                AudioData audioDate = new AudioData();
                audioDate.buffer = ByteBuffer.allocateDirect(bufferSizeInBytes);
                audioDate.size = audioRecord.read(audioDate.buffer, bufferSizeInBytes);
                audioDate.prevOutputPTSUs = getPTSUs();
                try {
                    if (weakReference.get().queue != null) {
                        weakReference.get().queue.put(audioDate);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (Debug) Log.e(TAG, "音频录制结束   ");
            audioRecord.stop();
        }


        /**
         * previous presentationTimeUs for writing
         */
        private long prevOutputPTSUs = 0;

        /**
         * get next encoding presentationTimeUs
         *
         * @return
         */
        protected long getPTSUs() {
            long result = System.nanoTime() / 1000L;
            // presentationTimeUs should be monotonic
            // otherwise muxer fail to write
            if (result < prevOutputPTSUs)
                result = (prevOutputPTSUs - result) + result;
            return result;
        }

        public void stopRecording() {
            isRecording = false;
        }

        public void release() {
            if (audioRecord != null && audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
                audioRecord.stop();
                audioRecord.release();
                audioRecord = null;
            }
        }
    }


    static class AudioEncodecThread extends Thread {

        private static final boolean Debug = true;
        private static final String TAG = AudioEncodecThread.class.getSimpleName();
        private WeakReference<AudioAacEncodecActivity> weakReference;
        private boolean isEncodec = false;


        private int sampleRateInHz;   //采样率
        private int channelConfig;   //声道数
        private int audioFormat;   //采样格式(存储位数， 8位 16位)
        private int bufferSizeInBytes;   //最小缓存大小

        //编码
        private MediaCodec audioEncode;

        private MediaCodec.BufferInfo audioBufferInfo;

        private FileOutputStream fileOutputStream = null;

        public AudioEncodecThread(WeakReference<AudioAacEncodecActivity> weakReference) {
            this.weakReference = weakReference;

            this.sampleRateInHz = weakReference.get().sampleRateInHz;
            this.channelConfig = weakReference.get().channelConfig;
            this.audioFormat = weakReference.get().audioFormat;
            initAudioEncodec();
        }

        private void initAudioEncodec() {
            audioBufferInfo = new MediaCodec.BufferInfo();
            bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
            try {
                audioEncode = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);

                MediaFormat audioFormat = MediaFormat.createAudioFormat(
                        MediaFormat.MIMETYPE_AUDIO_AAC,
                        sampleRateInHz,
                        1
                );
                audioFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
                audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
                audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);
                audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSizeInBytes);
                audioEncode.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);


            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void run() {
            super.run();


            audioEncode.start();
            isEncodec = true;

            Log.e(TAG, "音频编码开始 ");

            File file = new File(weakReference.get().targetFilePath);

            try {
                if (!file.exists()) {
                    file.createNewFile();
                }

                fileOutputStream = new FileOutputStream(file);

                AudioData audioData = null;
                while (isEncodec) {
                    audioData = getAudioData();

                    if (null == audioData) {
                        continue;
                    }

                    int inputBufferIndex = audioEncode.dequeueInputBuffer(0);
                    if (inputBufferIndex >= 0) {
                        ByteBuffer inputBuffer = audioEncode.getInputBuffer(inputBufferIndex);
                        inputBuffer.clear();
                        inputBuffer.put(audioData.buffer);

                        if (audioData.size > 0) {
                            audioEncode.queueInputBuffer(
                                    inputBufferIndex,
                                    0, audioData.size,
                                    audioData.prevOutputPTSUs,
                                    0
                            );
                        } else {//数据小于0  发送结束标志
                            audioEncode.queueInputBuffer(inputBufferIndex, 0, 0, audioData.prevOutputPTSUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        }
                    }

                    int outputBufferIndex = audioEncode.dequeueOutputBuffer(audioBufferInfo, 0);

                    Log.e(TAG, "获取队列索引值  " + outputBufferIndex);

                    if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                        //等待
                    } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                        //输出缓冲队列改变
                    } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                        //输出格式改变时，重新获取格式

                        /**这里暂时不做处理*/

                    } else {

                        ByteBuffer outputBuffer = audioEncode.getOutputBuffer(outputBufferIndex);

                        Log.e(TAG, "获取输出buffer  ");
                        int outBufferSize = outputBuffer.limit() + 7;
                        byte[] aacBytes = new byte[outBufferSize];
                        /**
                         * 添加ADTS头部，才能进行播放
                         */
                        addADTStoPacket(aacBytes, outBufferSize);

                        outputBuffer.get(aacBytes, 7, outputBuffer.limit());

                        fileOutputStream.write(aacBytes);
                        Log.e(TAG, "数据写入到文件  ");
                        audioEncode.releaseOutputBuffer(outputBufferIndex, false);
                    }

                }

                fileOutputStream.flush();
                fileOutputStream.close();

                release();
                Log.e(TAG, "音频编码结束 ");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        /**
         * 从队列中取出一帧待编码的音频数据
         *
         * @return
         */
        public AudioData getAudioData() {
            if (weakReference.get().queue != null) {
                try {
                    return (AudioData) weakReference.get().queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        private void addADTStoPacket(byte[] packet, int packetLen) {
            Log.e(TAG, "添加ADTS 头部  ");

            int profile = 2;  //AAC LC，MediaCodecInfo.CodecProfileLevel.AACObjectLC;
            int freqIdx = 4;  //见后面注释avpriv_mpeg4audio_sample_rates中32000对应的数组下标，来自ffmpeg源码
            int chanCfg = 1;  //见后面注释channel_configuration，AudioFormat.CHANNEL_IN_MONO 单声道(声道数量)


            // fill in ADTS data
            packet[0] = (byte) 0xFF;
            //packet[1] = (byte)0xF9;
            packet[1] = (byte) 0xF1;//解决ios 不能播放问题
            packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
            packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
            packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
            packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
            packet[6] = (byte) 0xFC;
        }

        public void stopEncodec() {
            isEncodec = false;
        }

        public void release() {
            if (null!=fileOutputStream){
                try {
                    fileOutputStream.flush();
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != audioEncode) {
                audioEncode.stop();
                audioEncode.release();
                audioEncode = null;
            }
        }
    }
}
